#include <cassert>

#include <fstream>
using std::ifstream;
using std::ios;

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

#include <list>
using std::list;

#include <map>
using std::map;

#include <string>
using std::string;

#include <utility>
using std::make_pair;
using std::pair;


#include "tbb/concurrent_hash_map.h"
using tbb::concurrent_hash_map;


typedef unsigned short int Worker_threads;

typedef string Reference_sequence;
/* Type: name of input sequence */
typedef string Sequence_name;
/* Type: input sequence */
typedef string Sequence;
/* Type: named input sequence */
typedef pair<Sequence_name, Sequence> Named_sequence;

/* Type: start position number */
typedef size_t Start_position;

/* Type: finish position number */
typedef size_t Finish_position;

/* Type: pattern position in reference */
typedef pair<Start_position, Finish_position> Pattern_position_in_reference_sequence;

/* Type: pattern position in input */
/* This order - for right sorting */
typedef pair<Finish_position, Start_position> Pattern_position_in_input_sequence;

/* Type: all positions of pattern in input */
typedef map<Finish_position, Start_position> Pattern_position_in_input_sequence;





/* Functors for test cases processing pipeline */
class Write_to_map_functor
{
	Pattern_position_in_input_sequence
public:
    MyOutputFunc( FILE* output_file );
    void operator()( TextSlice* item );
};
MyOutputFunc::MyOutputFunc( FILE* output_file ) :
    my_output_file(output_file)
{
}
void MyOutputFunc::operator()( TextSlice* out ) {
    size_t n = fwrite( out->begin(), 1, out->size(), my_output_file );
    if( n!=out->size() ) {
        fprintf(stderr,"Can't write into file '%s'\n", OutputFileName);
        exit(1);
    }
    out->free();
};



// Use concurrent hash map below
typedef map<Sequence_name, Sequence> Test_cases;

typedef size_t Hash;
typedef string Pattern;
typedef size_t Position;
typedef pair<Position, Position> Place_in_reference;
struct MyHashCompare
{ 
	static size_t hash( Hash const p )
	{
		return p;
	}
	static bool equal( Hash const x, Hash const y )
	{
		return x == y; 
	} 
};
typedef concurrent_hash_map<Hash, list<Place_in_reference>, MyHashCompare> Patterns_hash_map; 






Named_sequence get_filtered_sequence( ifstream & stream )
{
	Sequence_name name;
	Sequence sequence;

	if( stream.get() == '>' )
	{
		stream >> name;
	}

	while( !stream.eof() )
	{
		int c = stream.get();
		if( c == '>' )
		{
			stream.unget();
			break;
		}
		else if( c == 'A' || c == 'C' || c == 'G' || c == 'T' )
		{
			sequence.push_back( c );
		}
	}

	return make_pair( name, sequence );
}



int main( int argc, char * argv[] )
{
	// Number of worker threads to use
	Worker_threads worker_threads = static_cast<Worker_threads>( atoi( argv[ 1 ] ) );

	// Minimum length of substring
	size_t minimum_match_length = atol( argv[ 2 ] );
	assert( minimum_match_length > 5 );

	Pattern_hash_map patterns;
	// Reference sequence file
	{
		ifstream reference_sequence_ifstream( argv[ 3 ], ios::in );
		Reference_sequence reference_sequence;
		
		// Read and forget name
		if( reference_sequence_ifstream.get() == '>' )
		{
			Sequence_name name;
			reference_sequence_ifstream >> name;
		}
		
		size_t last_read_symbol_number = 0;
		
		// Symbol code
		u_short code;
		Hash hash = 0;
		while( !reference_sequence_ifstream.eof() && last_read_symbol_number != minimum_match_length )
		{
			int c = reference_sequence_ifstream.get();
			if( c == 'A' )
				code = 0;
			else if( c == 'C' )
				code = 1;
			else if( c == 'G' )
				code = 2;
			else if( c == 'T' )
				code = 3;
			else
				continue;
			
			if( code != 0 )
			{
				reference_sequence.push_back( (char)c );
				++last_read_symbol_number;
				hash <<= 2;
				hash += code;
			}
		}
		
		
		while( !reference_sequence_ifstream.eof() )
		{
			int c = reference_sequence_ifstream.get();
			if( c == '>' )
			{
				reference_sequence_ifstream.unget();
				break;
			}
			else
			{
				if( c == 'A' )
					code = 0;
				else if( c == 'C' )
					code = 1;
				else if( c == 'G' )
					code = 2;
				else if( c == 'T' )
					code = 3;
				else
					continue;
				
				if( code != 0 )
				{
					reference_sequence.push_back( (char)c );
					++last_read_symbol_number;
				}
			}
		}
	}

	// Test cases
	Test_cases test_cases;
	for( int i = 4; i < argc; ++i )
	{
		ifstream input_file( argv[ i ], ios::in );
		
	}
	
	return 0;
}
